package mysftp

import (
	"errors"
	"fmt"
	"gofile/encodejson"
	"log"
	"os"
	"path"
	"time"

	"github.com/pkg/sftp"
	"golang.org/x/crypto/ssh"
)

// 通过SSH连接Linux服务器
func keyOrPwdConnectLinuxServer(sshHost, sshUser, sshPassword, sshKey string, sshPort int) (*ssh.Client, error) {
	// 创建ssh登录配置
	config := &ssh.ClientConfig{
		Timeout:         5 * time.Second,             // 超时时间
		User:            sshUser,                     // 登录账号
		HostKeyCallback: ssh.InsecureIgnoreHostKey(), //这个不够安全，生产环境不建议使用
		// HostKeyCallback: ssh.FixedHostKey(), //建议使用这种
	}

	if sshHost == "" {
		log.Fatal("连接IP地址不能为空")
	}

	if sshUser == "" {
		log.Fatal("连接用户名不能为空")
	}

	if sshUser == "sshPort" {
		log.Fatal("连接端口不能为空")
	}

	if sshKey != "" {
		// 密钥登录
		key, err := os.ReadFile(sshKey)
		if err != nil {
			log.Fatal("sftp密钥读取失败")
		}
		// 会拿着密钥去登录服务器
		signer, err := ssh.ParsePrivateKey(key)
		if err != nil {
			log.Fatal("密钥签名失败")
		}
		// 配置密钥登录
		config.Auth = []ssh.AuthMethod{
			ssh.PublicKeys(signer),
		}
	} else if sshPassword != "" {
		// 密码登录
		config.Auth = []ssh.AuthMethod{ssh.Password(sshPassword)}
	} else {
		err := errors.New("sftp登录密钥或者登录密码不能为空")
		return nil, err
	}

	// dial连接服务器
	addr := fmt.Sprintf("%s:%d", sshHost, sshPort)
	Client, err := ssh.Dial("tcp", addr, config)
	if err != nil {
		log.Fatal("连接到服务器失败", err)
		return nil, err
	}

	return Client, nil
}

// 创建sftp会话
func CreateSftp(sshHost, sshUser, sshPassword, sshKey string, sshPort int) (*sftp.Client, error) {
	// 连接Linux服务器
	conn, err := keyOrPwdConnectLinuxServer(sshHost, sshUser, sshPassword, sshKey, sshPort)
	if err != nil {
		fmt.Println("连接Linux服务器失败")
		panic(err)
	}
	//defer conn.Close()

	// 创建sftp会话
	client, err := sftp.NewClient(conn)
	if err != nil {
		return nil, err
	}
	//defer client.Close()
	return client, nil
}

func Upload() {
	config, err := encodejson.GetConfig()
	if err != nil {
		log.Fatal("解码JSON配置出错", err)
		return
	}

	// 连接sftp
	client, err := CreateSftp(config.SshHost, config.SshUser, config.SshPassWord, config.SshKey, config.SshPort)
	if err != nil {
		return
	}
	defer client.Close()

	if config.LocalFilePath == "" {
		log.Fatal("打包后的zip目录不能为空")
	}

	if config.RemoteDir == "" {
		log.Fatal("部署的目录不能为空")
	}

	// 上传文件
	var localFilePath = config.LocalFilePath // 本地文件全路径
	var remoteDir = config.RemoteDir         // 服务器目录
	// 打开需要上传的本地文件
	srcFile, err := os.Open(localFilePath)
	if err != nil {
		panic(err)
	}
	defer srcFile.Close()
	// 获取需要上传的文件的名称
	var remoteFileName = path.Base(localFilePath)
	// 在服务器创建文件并打开文件获得句柄
	dstFile, err := client.Create(path.Join(remoteDir, remoteFileName))
	if err != nil {
		panic(err)
	}
	defer dstFile.Close()
	// 写入内容 创建了一个长度为1024的字节切片
	buf := make([]byte, 1024)
	for {
		// 把文件中的内容读取到buf中，每次读取本地需要上传的文件1024字节内容
		n, _ := srcFile.Read(buf)
		// 当n=0时，证明本地需要上传的文件已经读取完毕了
		if n == 0 {
			break
		}
		dstFile.Write(buf) // 写入到句柄
	}
	fmt.Println("文件上传完成")
}
